package display;

import simframe.simulation.Range;
import simframe.simulation.MetricDisplay;
import simframe.simulation.Metric;
import javax.swing.*;
import java.util.*;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;


import java.util.HashMap;
import java.util.Set;
import java.text.DecimalFormat;

import beantown.*;

@SuppressWarnings("serial")
public class EconosimDisplay extends javax.swing.JFrame {

    /**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static Color[] lineColors = new Color[] {Color.BLUE, Color.GREEN, Color.ORANGE, Color.RED, Color.PINK, Color.MAGENTA};
	
	private class Metrics extends Canvas implements MetricDisplay {
		
		DecimalFormat numFormat = new DecimalFormat("0");
		
		HashMap<Metric, List<Double>> metricDisplay;
		
		Set<String> metricNames;
		
		Map<String, Double> currentValues;
		Map<String, Range> ranges;
		
		public Map<String, Color> getLegend() {
			HashMap<String, Color> rval = new HashMap<String, Color>();
			
			int index = 0;
			for(Metric metric : metricDisplay.keySet()) {
				rval.put(metric.getName(), lineColors[index++ % lineColors.length]);
			}
			
			return rval;
		}
		
		public void init(java.util.List<Metric> metrics) {
			
			metricDisplay = new HashMap<Metric,List<Double>>();
			
			// create a label for each metric
			for(Metric metric : metrics) {
				metricDisplay.put(metric, new ArrayList<Double>());
			}
		}
		
		public synchronized void update(long t) {
			
			// update lists
			for(Metric metric : metricDisplay.keySet()) {
				
				double dval = metric.val();
				
				metricDisplay.get(metric).add(dval);
			}
			
			this.repaint();
		}
		
		@Override
		public void paint(Graphics g) {

			synchronized(metricDisplay) {
				this.setBackground(Color.WHITE);

				int index = 0;
				int offset = 0;
				for(Metric metric : metricDisplay.keySet()) {

					List<Double> vals = metricDisplay.get(metric);
					Range range = metric.getRange();

					g.setColor(lineColors[index % lineColors.length]);

					String maxStr = numFormat.format(range.getMax());
					String minStr = numFormat.format(range.getMin());

					g.drawString(minStr, offset, this.getHeight() - 2);
					g.drawString(maxStr, offset, 10);

					offset += 10 + 10 * maxStr.length();

					index++;

					int w = getWidth();
					int length = vals.size();

					int t = 0;
					int preval = Integer.MIN_VALUE;
					for(Double dval : vals) {

						// if t = length then x = w
						int x0 = w + (t - length);
						int curVal =  this.getHeight() - 1 - range.map(dval, 0, this.getHeight() - 2);

						if ( (preval > Integer.MIN_VALUE) && (x0 >= 0)) {
							//if (x0 >= 0) {
							g.drawLine(x0, preval, x0 + 1, curVal);
						}
						preval = curVal;
						++t;
					}
				}
			}
		}
	}
	
	private class SimCanvas extends Canvas implements simframe.simulation.SimViewer {
        /**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		public synchronized  void update(long t) {
            repaint();
        }

		@Override
		public  void paint(Graphics g) {

			synchronized(sim) {
				this.setBackground(Color.BLACK);

				// g.drawRect(100, 100, 20, 20);
//				for(Producer firm : sim.firms) {
//					int x = 0;//firm.pos[0];
//					int y = 0; //firm.pos[1];
//
//					g.setColor(Color.RED);
//
//					if (firm.dead()) {
//						g.setColor(Color.YELLOW);
//					}
//					g.fillOval(x,y, 10, 10);
//				}

//				for(Consumer con : sim.cons) {
//					int x = con.pos[0];
//					int y = con.pos[1];
//					g.setColor(Color.GREEN);
//
//					if (con.dead()) {
//						g.setColor(Color.BLUE);
//					}
//					g.fillOval(x, y, 5, 5);
//				}

//				for (Consumer con : sim.cons) {
//					int index = 0;
//					for (Producer firm : con.firms) {
//						g.setColor(lineColors[index % lineColors.length]);
//						g.drawLine(con.pos[0], con.pos[1], firm.pos[0], firm.pos[1]);
//						index++;
//					}
//				}
			}
		}

        public void reset() {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    }


    final Sim sim;
    Thread simThread;

      // this is days per second
    static final int minDays = 1;
    static final int maxDays = 30;

    /** Creates new form EonoSim */
    public EconosimDisplay() {
    	
     

        sim = new Sim(1000, 100, 600, 600);

        sim.updateMargin(0.5);
        sim.updateDole(0.5);

        SimCanvas simCanvas = new SimCanvas();
        simCanvas.setBounds(0, 0, 600, 600);

        sim.addViewer(simCanvas, 7);



        simCanvas.getHeight();
        simCanvas.getWidth();
          
        javax.swing.JPanel topPanel = new javax.swing.JPanel(new GridBagLayout ());
        this.add(topPanel);
        GridBagConstraints c;
        
        // buttons
        javax.swing.JPanel buttonPanel = addButtons();
        c = new GridBagConstraints();
        //natural height, maximum width
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridx = 0;
        c.gridy = 0;
        // c.gridwidth = 2;
        
        topPanel.add(buttonPanel, c);
     
        // Canvas with Picture
        c = new GridBagConstraints();
   		
        //natural height, maximum width
        // c.fill = GridBagConstraints.RELATIVE;
        c.gridx = 0;
        c.gridy = 1;
        // c.gridwidth = 2;
        topPanel.add(simCanvas, c);
        
        // parameter panel
        javax.swing.JPanel sliderPanel = addParameters();
        //natural height, maximum width
        c = new GridBagConstraints();
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridx = 0;
        c.gridy = 2;
        // c.gridwidth = 2;
        
        topPanel.add(sliderPanel, c);
        
        javax.swing.JPanel metricPanel = addMetrics();
        c = new GridBagConstraints();
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridx = 0;
        c.gridy = 3;
       // c.gridwidth = 2;
        c.weighty = 1.0;   //request any extra vertical space
        c.anchor = GridBagConstraints.PAGE_END; //bottom of space
        
        topPanel.add(metricPanel, c);
       
        
        setDaysToRun();
        updateWelfare(null);
        profitShareSliderStateChanged(null);
        
        this.pack();
        this.setResizable(false);
    }

    private javax.swing.JPanel addButtons() {

    	javax.swing.JPanel panel = new javax.swing.JPanel(new GridBagLayout());

    	// Start Button
    	startButton = new javax.swing.JButton();

    	startButton.setText("Start");

    	startButton.addActionListener(new java.awt.event.ActionListener() {
    		public void actionPerformed(java.awt.event.ActionEvent evt) {
    			jButton1ActionPerformed(evt);
    		}
    	});

    	panel.add(startButton);

    	// End Button
    	stopButton = new javax.swing.JButton();

    	stopButton.setText("Pause");
    	stopButton.setEnabled(false);

    	stopButton.addActionListener(new java.awt.event.ActionListener() {
    		public void actionPerformed(java.awt.event.ActionEvent evt) {
    			jButton2ActionPerformed(evt);
    		}
    	});
    	
    	panel.add(stopButton);


    	return panel;
    }
    
    private javax.swing.JPanel addParameters() {
    	 javax.swing.JPanel sliderPanel = new javax.swing.JPanel(new GridBagLayout ());
     
        
         //
         // Sliders
         //
         simLabel = new javax.swing.JLabel();
         simLabel.setText("Simulation Speed");
         
         GridBagConstraints c = new GridBagConstraints();
         c.fill = GridBagConstraints.LINE_END;
         c.weightx = 0.5;
         c.gridx = 0;
         c.gridy = 0;
         sliderPanel.add(simLabel, c);
         
         jSlider1 = new javax.swing.JSlider();
         
         jSlider1.addChangeListener(new javax.swing.event.ChangeListener() {
             public void stateChanged(javax.swing.event.ChangeEvent evt) {
             	jSlider1StateChanged(evt);
             }
         });
       
         c = new GridBagConstraints();
         c.fill = GridBagConstraints.HORIZONTAL;
         c.weightx = 0.5;
         c.gridx = 1;
         c.gridy = 0;
         sliderPanel.add(jSlider1, c);
         
         // Profit Sharing
         shareLabel = new javax.swing.JLabel();
         shareLabel.setText("Profit Sharing");
         c = new GridBagConstraints();
         c.fill = GridBagConstraints.LINE_END;
         c.weightx = 0.5;
         c.gridx = 0;
         c.gridy = 1;
         sliderPanel.add(shareLabel, c);
         
         profitShareSlider = new javax.swing.JSlider();
         profitShareSlider.addChangeListener(new javax.swing.event.ChangeListener() {
             public void stateChanged(javax.swing.event.ChangeEvent evt) {
                 profitShareSliderStateChanged(evt);
             }
         });
         
         c = new GridBagConstraints();
         c.fill = GridBagConstraints.HORIZONTAL;
         c.weightx = 0.5;
         c.gridx = 1;
         c.gridy = 1;
         sliderPanel.add(profitShareSlider, c);
         
         // Subsidy
         
         doleLabel = new javax.swing.JLabel();
         doleLabel.setText("Subsidy Level");
         c = new GridBagConstraints();
         c.fill = GridBagConstraints.LINE_END;
         c.weightx = 0.5;
         c.gridx = 0;
         c.gridy = 2;
         sliderPanel.add(doleLabel, c);
   
         WelfareSlider = new javax.swing.JSlider();
         WelfareSlider.addChangeListener(new javax.swing.event.ChangeListener() {
             public void stateChanged(javax.swing.event.ChangeEvent evt) {
                 updateWelfare(evt);
             }
         });
         
         c = new GridBagConstraints();
         c.fill = GridBagConstraints.HORIZONTAL;
         c.weightx = 0.5;
         c.gridx = 1;
         c.gridy = 2;
         sliderPanel.add(WelfareSlider, c);
         
         return sliderPanel;
    }
    
    private javax.swing.JPanel addMetrics() {
    	
        Metrics metrics = new Metrics();
        metrics.setBounds(0, 0, 600, 200);
        sim.addMetricDisplay(metrics);
        
    	javax.swing.JPanel panel = new javax.swing.JPanel(new GridBagLayout());
    	
    	  //natural height, maximum width
    	GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridx = 1;
        c.gridy = 0;
        
        panel.add(metrics, c);
 
        // legend
        
        Map<String, Color> legend = metrics.getLegend();
        javax.swing.JPanel legendPanel = new javax.swing.JPanel(new GridLayout(legend.size(), 1));
        
        for(String name : legend.keySet()) {
        	JLabel label = new JLabel();

        	label.setText(name);
        	label.setForeground(legend.get(name));

        	legendPanel.add(label);
        }
        
        c = new GridBagConstraints();
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridx = 0;
        c.gridy = 0;
        
        c.weightx = 0;
      
        
        panel.add(legendPanel, c);
    	
    	return panel;
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
 


    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed

    	stopButton.setEnabled(true);
    	startButton.setEnabled(false);
    	stopButton.setText("Pause");
    	  
        sim.reset();

        setDaysToRun();
        
        simThread = new Thread(sim);
        
        simThread.start();

    }//GEN-LAST:event_jButton1ActionPerformed

    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed

    	if (sim.stopped()) {
    		  simThread = new Thread(sim);
    	        
    	        simThread.start();
    	        stopButton.setText("Pause");
    	    	startButton.setEnabled(false);
    	}
    	else {
        	sim.stop();
        	simThread = null;
        	stopButton.setText("Continue");
        	startButton.setText("Restart");
        	startButton.setEnabled(true);
    	}
    
    }//GEN-LAST:event_jButton2ActionPerformed


    private void updateWelfare(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_updateWelfare
     int x = this.WelfareSlider.getValue();

     java.text.DecimalFormat twoPlaces = new java.text.DecimalFormat("0.00");
     
     double aveDole = sim.updateDole( x/100.0);
     
     doleLabel.setText("Dollars per day: " + twoPlaces.format(aveDole));
     
    }//GEN-LAST:event_updateWelfare

    
    private void profitShareSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_profitShareSliderStateChanged
        int x = this.profitShareSlider.getValue();

        sim.updateMargin( x/100.0);
        
        shareLabel.setText("Profit Share: " + Integer.toString(x) + "%");
    }//GEN-LAST:event_profitShareSliderStateChanged


private void setDaysToRun()
{
     // this is the simulation speed
    int numDays = (int) (minDays + (maxDays * jSlider1.getValue()/100.0));
    simLabel.setText("Days per second: " + Integer.toString(numDays));
        sim.DaysPerSecond( numDays);
        System.out.println("Sim Speed = " + Integer.toString(numDays) + " days per second");
}
    private void jSlider1StateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_jSlider1StateChanged
      
        setDaysToRun();
    }//GEN-LAST:event_jSlider1StateChanged

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new EconosimDisplay().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JSlider WelfareSlider;
    private javax.swing.JSlider jSlider1;
    private javax.swing.JSlider profitShareSlider;
    javax.swing.JButton stopButton;
    javax.swing.JButton startButton ;
    javax.swing.JLabel simLabel;
    javax.swing.JLabel shareLabel;
    javax.swing.JLabel doleLabel;
    // End of variables declaration//GEN-END:variables

}

